package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.lkd.common.VMSystem;
import com.lkd.config.ConsulConfig;
import com.lkd.config.TopicConfig;
import com.lkd.contract.*;
import com.lkd.dao.VendingMachineDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.*;
import com.lkd.exception.LogicException;
import com.lkd.http.vo.CreateVMReq;
import com.lkd.service.*;
import com.lkd.utils.DistributedLock;
import com.lkd.utils.UUIDUtils;
import com.lkd.vo.Pager;
import com.lkd.vo.SkuVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Transactional
@Service
@Slf4j
public class VendingMachineServiceImpl extends ServiceImpl<VendingMachineDao, VendingMachineEntity> implements VendingMachineService {
    @Autowired
    private ConsulConfig consulConfig;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    @Autowired
    VendoutRunningService vendoutRunningService;

    @Autowired
    private NodeService nodeService;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private VmTypeService vmTypeService;

    @Autowired
    private SkuService skuService;

    @Autowired
    private MqttProducer mqttProducer;

    /**
     * @param vendingMachine
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean add(CreateVMReq vendingMachine) {
        VendingMachineEntity vendingMachineEntity = new VendingMachineEntity();
        vendingMachineEntity.setNodeId(Long.valueOf(vendingMachine.getNodeId()));
        vendingMachineEntity.setVmType(vendingMachine.getVmType());
        NodeEntity nodeEntity = nodeService.getById(vendingMachine.getNodeId());
        if (nodeEntity == null) {
            throw new LogicException("所选点位不存在");
        }
        //复制属性
        BeanUtils.copyProperties(nodeEntity, vendingMachineEntity);
        vendingMachineEntity.setCreateUserId(Long.valueOf(vendingMachine.getCreateUserId()));
        vendingMachineEntity.setInnerCode(UUIDUtils.getUUID());
        vendingMachineEntity.setClientId(UUIDUtils.generateClientId(vendingMachineEntity.getInnerCode()));
        this.save(vendingMachineEntity);
        //创建货道数据
        createChannel(vendingMachineEntity);
        return true;
    }


    /**
     * 创建货道
     *
     * @param vm
     * @return
     */
    private boolean createChannel(VendingMachineEntity vm) {
        VmTypeEntity vmType = vmTypeService.getById(vm.getVmType());
        List<ChannelEntity> channelList = Lists.newArrayList();
        for (int i = 1; i <= vmType.getVmRow(); i++) {
            for (int j = 1; j <= vmType.getVmCol(); j++) {
                ChannelEntity channel = new ChannelEntity();
                channel.setChannelCode(i + "-" + j);
                channel.setCurrentCapacity(0);
                channel.setInnerCode(vm.getInnerCode());
                channel.setLastSupplyTime(vm.getLastSupplyTime());
                channel.setMaxCapacity(vmType.getChannelMaxCapacity());
                channel.setVmId(vm.getId());
                channelList.add(channel);
            }
        }
        channelService.saveBatch(channelList);
        return true;
    }


    @Override
    public boolean update(Long id, Long nodeId) {
        VendingMachineEntity vm = this.getById(id);
        if (vm.getVmStatus() == VMSystem.VM_STATUS_RUNNING)
            throw new LogicException("改设备正在运营");
        NodeEntity nodeEntity = nodeService.getById(nodeId);
        BeanUtils.copyProperties(nodeEntity, vm);
        return this.updateById(vm);
    }


    @Override
    public Pager<String> getAllInnerCodes(boolean isRunning, long pageIndex, long pageSize) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<VendingMachineEntity> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageIndex, pageSize);

        QueryWrapper<VendingMachineEntity> qw = new QueryWrapper<>();
        if (isRunning) {
            qw.lambda()
                    .select(VendingMachineEntity::getInnerCode)
                    .eq(VendingMachineEntity::getVmStatus, 1);
        } else {
            qw.lambda()
                    .select(VendingMachineEntity::getInnerCode)
                    .ne(VendingMachineEntity::getVmStatus, 1);
        }
        this.page(page, qw);
        Pager<String> result = new Pager<>();
        result.setCurrentPageRecords(page.getRecords().stream().map(VendingMachineEntity::getInnerCode).collect(Collectors.toList()));
        result.setPageIndex(page.getCurrent());
        result.setPageSize(page.getSize());
        result.setTotalCount(page.getTotal());

        return result;
    }

    @Override
    public Pager<VendingMachineEntity> query(Long pageIndex, Long pageSize, Integer status, String innerCode) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<VendingMachineEntity> page
                = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageIndex, pageSize);
        LambdaQueryWrapper<VendingMachineEntity> queryWrapper = new LambdaQueryWrapper<>();
        if (status != null) {
            queryWrapper.eq(VendingMachineEntity::getVmStatus, status);
        }
        if (!Strings.isNullOrEmpty(innerCode)) {
            queryWrapper.likeLeft(VendingMachineEntity::getInnerCode, innerCode);
        }
        this.page(page, queryWrapper);

        return Pager.build(page);
    }


    @Override
    public VmVO findByInnerCode(String innerCode) {
        LambdaQueryWrapper<VendingMachineEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VendingMachineEntity::getInnerCode, innerCode);
        VendingMachineEntity vm = this.getOne(queryWrapper);
        VmVO vmVO = new VmVO();

        BeanUtils.copyProperties(vm, vmVO, "areaId");
        vmVO.setNodeAddr(vm.getNode().getAddr());//地址
        vmVO.setNodeName(vm.getNode().getName());//名称
        return vmVO;
    }


    @Override
    public boolean updateStatus(String innerCode, Integer status) {
        try {
            UpdateWrapper<VendingMachineEntity> uw = new UpdateWrapper<>();
            uw.lambda()
                    .eq(VendingMachineEntity::getInnerCode, innerCode)
                    .set(VendingMachineEntity::getVmStatus, status);
            this.update(uw);

        } catch (Exception ex) {
            log.error("updateStatus error,innerCode is " + innerCode + " status is " + status, ex);
            return false;
        }
        return true;
    }


    @Override
    public List<SkuVO> getSkuListByInnerCode(String innerCode) {
        //获取货道列表
        List<ChannelEntity> channelList = channelService.getChannelesByInnerCode(innerCode).stream()
                .filter(c -> c.getSkuId() > 0 && c.getSku() != null).collect(Collectors.toList());
        //获取有商品的库存余量
        Map<SkuEntity, Integer> skuMap = channelList.stream()
                .collect(Collectors.groupingBy(
                        ChannelEntity::getSku,
                        Collectors.summingInt(ChannelEntity::getCurrentCapacity)));//对库存数求和
        return skuMap.entrySet().stream().map(entry -> {
            SkuEntity sku = entry.getKey(); //查询商品
            SkuVO skuVO = new SkuVO();
            BeanUtils.copyProperties(sku, skuVO);
            skuVO.setImage(sku.getSkuImage());//图片
            skuVO.setCapacity(entry.getValue());
            skuVO.setRealPrice(sku.getPrice());//真实价格
            return skuVO;
        }).sorted(Comparator.comparing(SkuVO::getCapacity).reversed())  //按库存量降序排序
                .collect(Collectors.toList());
    }

    @Override
    public Boolean hasCapacity(String innerCode, Long skuId) {
        var qw = new LambdaQueryWrapper<ChannelEntity>();
        qw
                .eq(ChannelEntity::getInnerCode, innerCode)
                .eq(ChannelEntity::getSkuId, skuId)
                .gt(ChannelEntity::getCurrentCapacity, 0);
        return channelService.count(qw) > 0;
    }

    /**
     * 更新售货机库存
     *
     * @param supplyContract
     */
    @Override
    public boolean supply(SupplyContract supplyContract) {
        //获取售货机编号
        String innerCode = supplyContract.getInnerCode();
        //获取补货详情
        List<SupplyChannel> supplyChannels = supplyContract.getSupplyData();
        //1.修改售货机,补货时间
        this.update(Wrappers.<VendingMachineEntity>lambdaUpdate()
                .eq(VendingMachineEntity::getInnerCode, innerCode)
                .set(VendingMachineEntity::getLastSupplyTime, LocalDateTime.now())
        );
        //2.获取当前售货机全部货道
        List<ChannelEntity> channelEntities = channelService.list(Wrappers.<ChannelEntity>lambdaQuery()
                .eq(ChannelEntity::getInnerCode, innerCode)
        );

        channelEntities.stream().forEach(channelEntity ->
                {
                    SupplyChannel supplyChannel1 = supplyChannels.stream().filter(supplyChannel ->
                            supplyChannel.getChannelId().equals(channelEntity.getChannelCode())
                    ).collect(Collectors.toList()).get(0);
                    channelEntity.setCurrentCapacity(channelEntity.getCurrentCapacity() + supplyChannel1.getCapacity());
                    //修改货道补货时间
                    channelEntity.setLastSupplyTime(LocalDateTime.now());
                    channelService.updateById(channelEntity);
                }
        );

        return true;

    }

    /**
     * 根据售货机编号发送补货内容到工单服务
     *
     * @param innerCode
     */
    @Override
    public void sendSupplyTask(String innerCode) {
        //查询当前售货机在使用的货道
        List<ChannelEntity> channelEntities = channelService.list(Wrappers.<ChannelEntity>lambdaQuery()
                //售货机编号
                .eq(ChannelEntity::getInnerCode, innerCode)
                //售货机货道,没有确定商品
                .ne(ChannelEntity::getSkuId, 0L)
        );

        List<SupplyChannel> supplyChannels = channelEntities.stream()
                .filter(channelEntity ->
                        channelEntity.getCurrentCapacity() < channelEntity.getMaxCapacity()
                )
                .map(channelEntity -> {
                    SupplyChannel supplyChannel = new SupplyChannel();
                    supplyChannel.setChannelId(channelEntity.getChannelCode());
                    supplyChannel.setCapacity(channelEntity.getMaxCapacity() - channelEntity.getCurrentCapacity());
                    supplyChannel.setSkuId(channelEntity.getSkuId());
                    //根据商品id查询商品
                    SkuEntity skuEntity = skuService.getById(channelEntity.getSkuId());
                    supplyChannel.setSkuImage(skuEntity.getSkuImage());
                    supplyChannel.setSkuName(skuEntity.getSkuName());

                    return supplyChannel;
                }).collect(Collectors.toList());

        if (supplyChannels.size() > 0) {
            //有需要补货的轨道
            //协议封装
            SupplyContract supplyContract = new SupplyContract();
            supplyContract.setInnerCode(innerCode);
            supplyContract.setSupplyData(supplyChannels);


            try {
                mqttProducer.send(TopicConfig.TASK_SUPPLY_TOPIC, 2, supplyContract);
            } catch (Exception e) {
                throw new LogicException("自动发送补货内容错误");
            }

        }
    }


    /**
     * 下单成功后，修改库存，保存流水
     *
     * @param vendoutContract
     * @return
     */
    @Override
    public boolean vendout(VendoutContract vendoutContract) {
        try {
            //售货机号
            String innerCode = vendoutContract.getInnerCode();
            //商品id
            VendoutData vendoutData = vendoutContract.getVendoutData();
            long skuId = vendoutData.getSkuId();
            //根据，售货机号，商品id获取货道信息
            List<ChannelEntity> channels = channelService.list(
                    Wrappers.<ChannelEntity>lambdaQuery()
                            .eq(ChannelEntity::getInnerCode, innerCode)
                            .eq(ChannelEntity::getSkuId, skuId)
                            .gt(ChannelEntity::getCurrentCapacity, 0)
            );
            //没有查询到
            if (channels.size() <= 0) {
                log.info("缺货");
                return false;
            }
            //查询到修改巷道数据
            ChannelEntity channelEntity = channels.get(0);
            channelEntity.setCurrentCapacity(channelEntity.getCurrentCapacity() - 1);
            channelService.updateById(channelEntity);
            //创建流水
            VendoutRunningEntity vendoutRunningEntity = new VendoutRunningEntity();
            vendoutRunningEntity.setInnerCode(innerCode);
            vendoutRunningEntity.setOrderNo(vendoutData.getOrderNo());
            vendoutRunningEntity.setSkuId(vendoutData.getSkuId());
            //出货状态
            vendoutRunningEntity.setStatus(true);
            //保存流水
            vendoutRunningService.save(vendoutRunningEntity);

            //向售货机终端发送消息，出货
            vendoutData.setChannelCode(channelEntity.getChannelCode());
            vendoutContract.setVendoutData(vendoutData);
            mqttProducer.send(TopicConfig.getVendoutTopic(innerCode), 2, vendoutContract);
            //解锁售货机状态
            DistributedLock lock = new DistributedLock(
                    consulConfig.getConsulRegisterHost(),
                    consulConfig.getConsulRegisterPort()
            );
            //从redis中通过键获取锁值
            String sessionId = redisTemplate.boundValueOps(VMSystem.VM_LOCK_KEY_PREF + vendoutContract.getInnerCode()).get();
            //释放锁
            lock.releaseLock(sessionId);


        } catch (Exception e) {

            log.error("update vendout result error.", e);

            return false;
        }

        return true;
    }


    /**
     * 修改流水状态为，出货
     *
     * @param vendoutResultContract
     * @return
     */
    @Override
    public boolean vendoutResult(VendoutResultContract vendoutResultContract) {
        if (!vendoutResultContract.isSuccess()) {
            log.info(vendoutResultContract.getInnerCode() + "出货异常，进入退款流程");
            //回滚数据,货道商品+1
            ChannelEntity channelEntity = channelService.getOne(
                    Wrappers.<ChannelEntity>lambdaUpdate()
                            .eq(ChannelEntity::getInnerCode, vendoutResultContract.getInnerCode())
                            .eq(ChannelEntity::getChannelCode, vendoutResultContract.getVendoutData().getChannelCode())
            );
            channelEntity.setCurrentCapacity(channelEntity.getCurrentCapacity() + 1);
            channelService.updateById(channelEntity);

            return false;
        } else {


            //根据订单编号获取流水
            String orderNo = vendoutResultContract.getVendoutData().getOrderNo();
            VendoutRunningEntity vendoutRunningEntity = vendoutRunningService.getOne(
                    Wrappers.<VendoutRunningEntity>lambdaQuery()
                            .eq(VendoutRunningEntity::getOrderNo, orderNo)
            );
            if (vendoutResultContract == null) {
                return false;
            }
            //售货机终端出货状态
            vendoutRunningEntity.setStatus(true);
            vendoutRunningService.updateById(vendoutRunningEntity);


            return true;

        }
    }
}
